/* Include these
   #include <dirent.h>
   #include <uuid/uuid.h>
   #include <stdint.h>
   #include <nit/list.h>
   #include <nit/hset.h>
   #include <nit/hmap.h>
*/

typedef struct Bbu_expr Bbu_expr;
typedef struct Bbu_elist Bbu_elist;
typedef struct Bbu_u Bbu_u;
typedef void (Bbu_bf)(void *, Bbu_u *); /* Binary function */
typedef struct Bbu_f Bbu_f;

enum bbu_ftype { BBU_E, BBU_D, BBU_C };

enum bbu_err { BBU_ERR_NONE = 0, BBU_ERR_MEM, BBU_ERR_DLOPEN, BBU_ERR_DIRFD,
	       BBU_ERR_OPEN, BBU_ERR_WRITE, BBU_ERR_INVALID, BBU_ERR_CLOSE,
	       BBU_ERR_READ, BBU_ERR_DLSYM, BBU_ERR_NEW_DIR, BBU_ERR_OPENDIR,
	       BBU_ERR_UNLINK };

#define BBU_END 1
enum bbu_etype { BBU_FA = BBU_END + 1, BBU_VAR, BBU_BLOB };

typedef uuid_t Bbu_vid;
typedef uuid_t Bbu_uid;

typedef struct {
	Bbu_uid f_id;
	Bbu_elist *args;
} Bbu_fa;  /* function applied */

typedef struct {
	uint64_t size;
	char *bytes;
} Bbu_blob;

struct Bbu_expr {
	enum bbu_etype type;
	union {
		Bbu_vid var;
		Bbu_fa fa;
		Bbu_blob blob;
	} i;
};

struct Bbu_elist {
	Nit_list list;
	Bbu_expr *expr;
};

struct Bbu_u {
	DIR *dir;
	int dir_fd;
	Nit_hmap *load;
	Nit_hmap *libs;
	Nit_hentry *stack;
}; /* universe, all unique values */

typedef struct {
	char *path;
	void *handle;
} Bbu_dlib;

/* ef: Executable function, expected to be present in executable */
/* df: Dynamic function, expected to be loadable from shared object */

typedef struct {
	Bbu_bf *bf;
        Bbu_dlib *lib;
        char *sym;
} Bbu_df;

struct Bbu_f {
        Bbu_uid id;
	enum bbu_ftype type;
	int save;
	union {
		Bbu_bf *e;
		Bbu_df *d;
		Bbu_elist *bod;
	} i; /* Implementation */
};

/* expr */
Bbu_expr *
bbu_expr_var(Bbu_vid var);

Bbu_expr *
bbu_expr_fa(Bbu_uid f_id, Bbu_elist *args);

Bbu_expr *
bbu_expr_blob(void *bytes, uint64_t size);

Bbu_expr *
bbu_expr_blob_copy(const void *bytes, uint64_t size);

void
bbu_expr_free(Bbu_expr *expr);

/* elist */
Bbu_elist *
bbu_elist_new(Bbu_expr *expr, Bbu_elist *next);

Bbu_elist *
bbu_nest(Bbu_expr *expr);

Bbu_elist *
bbu_elist_va(Bbu_expr *first, ...);

void
bbu_elist_free(Bbu_elist *list);

void
bbu_elist_free_all(Bbu_elist *list);

/* u */
Bbu_u *
bbu_u_new(const char *dir_name, enum bbu_err *err);

void
bbu_u_free(Bbu_u *u);

int
bbu_u_add(Bbu_u *u, Bbu_f *f);

Bbu_f *
bbu_u_remove(Bbu_u *u, Bbu_uid id);

enum bbu_err
bbu_u_delete(Bbu_u *u, Bbu_uid id);

enum bbu_err
bbu_u_ser(Bbu_u *u, Bbu_f *f);

Bbu_f *
bbu_u_deser(Bbu_u *u, Bbu_uid id, enum bbu_err *err);

Bbu_f *
bbu_u_get(Bbu_u *u, Bbu_uid id, enum bbu_err *err);

/* f */
Bbu_f *
bbu_ef(Bbu_uid id, Bbu_bf bf);

Bbu_f *
bbu_df(Bbu_u *u, Bbu_uid id, const char *lib, const char *sym, int save,
       enum bbu_err *err);

Bbu_f *
bbu_cf(Bbu_uid id, Bbu_elist *bod, int save);

void
bbu_f_new_id(Bbu_f *f, Bbu_uid id);

void
bbu_f_free(Bbu_f *f);
